package ch.subgate.spritestudio.widgets;

import java.util.ArrayList;

import ch.subgate.spritestudio.tools.OnHueChangedListener;
import ch.subgate.spritestudio.tools.OnSaturationChangedListener;
import ch.subgate.spritestudio.tools.OnValueChangedListener;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ComposeShader;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class SVPickerView extends View implements OnHueChangedListener {
	
	private float mWidth;
	private float mHeight;
	
	/** HSV color */
	private float[] mColor;
	
	/** Storing the x coordinate when a selection is made */
	private float mSelectX;
	
	/** Storing the y coordinate when a selection is made */
	private float mSelectY;
	
	/** Paint object used to draw the picker */
	private Paint mPaint;
	
	private Paint mIndicatorPaint;
	
	/** Currently selected color */
	private float[] mSelectedColor;
	
	/** Saturation listeners */
	private ArrayList<OnSaturationChangedListener> mSaturationListeners;
	
	/** Value listeners */
	private ArrayList<OnValueChangedListener> mValueListeners;

	public SVPickerView(Context context, AttributeSet attrs) {
		super(context, attrs);
		
		mWidth = 0.0f;
		mHeight = 0.0f;
		
		//Set the color to full red as default
		mColor = new float[3];
		mColor[0] = 1.0f;
		mColor[1] = 1.0f;
		mColor[2] = 1.0f;
		
		//Initialize the selected color
		mSelectedColor = new float[3];
		mSelectedColor[0] = 1.0f;
		mSelectedColor[1] = 0.0f;
		mSelectedColor[2] = 0.0f;
		
		//Instantiating the paint object
		mPaint = new Paint();
		mIndicatorPaint = new Paint();
		
		//Set select coordinations
		mSelectX = 0.0f;
		mSelectY = 0.0f;
		
		//Initialize the list of listeners
		mSaturationListeners = new ArrayList<OnSaturationChangedListener>();
		mValueListeners = new ArrayList<OnValueChangedListener>();
	}
	
	/**
	 * Method is called upon drawing
	 */
	@Override
	public void onDraw(Canvas canvas) {
		//Preparing the brightness shader which will be combined later
		Shader value = new LinearGradient(
				0.0f,
				0.0f,
				mWidth,
				0.0f,
				0xff000000,
				0xffffffff,
				Shader.TileMode.CLAMP);
		Shader saturation = new LinearGradient(
				0.0f,
				0.0f,
				0.0f,
				mHeight,
				0xffffffff,
				Color.HSVToColor(mColor),
				Shader.TileMode.CLAMP);
		
		ComposeShader shader = new ComposeShader(
				value,
				saturation,
				PorterDuff.Mode.MULTIPLY);
		
		mPaint.setShader(shader);
		
		canvas.drawRect(
				0.0f,
				0.0f,
				mWidth,
				mHeight,
				mPaint);
		
		// Drawing the indicator
		this.drawIndicator(canvas);
	}
	
	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		mWidth = (float)w;
		mHeight = (float)h;
	}
	
	/** Method is invoked when the view receives a touch event */
	@Override
	public boolean onTouchEvent(MotionEvent e) {
		float eventx, eventy;
		
		if (e.getX() > mWidth) {
			eventx = mWidth;
		}
		else if (e.getX() < 0.0f) {
			eventx = 0.0f;
		}
		else {
			eventx = e.getX();
		}
		
		if (e.getY() > mHeight) {
			eventy = mHeight;
		}
		else if (e.getY() < 0.0f) {
			eventy = 0.0f;
		}
		else {
			eventy = e.getY();
		}
		
		switch(e.getAction() & MotionEvent.ACTION_MASK) {
		case MotionEvent.ACTION_DOWN:
		case MotionEvent.ACTION_MOVE:
			mSelectX = eventx;
			mSelectY = eventy;
			break;
		case MotionEvent.ACTION_UP:
			//Store the touch coordinates
			mSelectX = eventx;
			mSelectY = eventy;
			//Update the S component
			mSelectedColor[1] = mSelectY / mHeight;
			//Update the V component
			mSelectedColor[2] = mSelectX / mWidth;
			
			onSaturationChanged();
			onValueChanged();
			break;
		}
		//Request the surface to redraw
		invalidate();
		return true;
	}
	
	public void drawIndicator(Canvas canvas) {
		mIndicatorPaint.setColor(0x99000000);
		canvas.drawRect(
				mSelectX - 6.0f, mSelectY - 6.0f,
				mSelectX + 6.0f, mSelectY + 6.0f,
				mIndicatorPaint);
		mIndicatorPaint.setColor(0xffffffff);
		canvas.drawRect(
				mSelectX - 1.0f, mSelectY - 1.0f,
				mSelectX + 1.0f, mSelectY + 1.0f,
				mIndicatorPaint);
	}
	
	/** Method is invoked when saturation component has changed */
	public void onSaturationChanged() {
		if (!mSaturationListeners.isEmpty()) {
			for (OnSaturationChangedListener listener : mSaturationListeners) {
				listener.onSaturationChanged(mSelectedColor[1]);
			}
		}
	}
	
	/** Method is invoked when value component has changed */
	public void onValueChanged() {
		if (!mValueListeners.isEmpty()) {
			for (OnValueChangedListener listener : mValueListeners) {
				listener.onValueChanged(mSelectedColor[2]);
			}
		}
	}
	
	/**
	 * Method is invoked when object receives a hue changed message
	 */
	@Override
	public void onHueChanged(float hue) {
		//Set H for the base color
		mColor[0] = hue;
		
		//Set H for the selected color
		mSelectedColor[0] = hue;
		
		//Invalidate to make the surface redraw
		invalidate();
	}
	
	/** Setter method for saturation component */
	public void setSaturation(float saturation) {
		mSelectedColor[1] = saturation;
		mSelectY = mSelectedColor[1] * mHeight;
		onSaturationChanged();
		invalidate();
	}
	
	/** Setter method for value component */
	public void setValue(float value) {
		mSelectedColor[2] = value;
		mSelectX = mSelectedColor[2] * mWidth;
		onValueChanged();
		invalidate();
	}
	
	/**
	 * Setter for the saturation listener
	 */
	public void registerSaturationListener(OnSaturationChangedListener listener) {
		mSaturationListeners.add(listener);
	}
	
	/**
	 * Setter for the value listener
	 */
	public void registerValueListener(OnValueChangedListener listener) {
		mValueListeners.add(listener);
	}
}
